package com.lamda.worker.command;

import com.lamda.worker.model.Snapshot;
import com.lamda.worker.model.Vps;
import com.lamda.worker.vo.ResultVo;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * Created by MJiao on 15/8/24.
 */
@Component
public class CommandExecutor {

    public static final org.apache.log4j.Logger LOGGER = Logger.getLogger(CommandExecutor.class);

    /**
     * 命令执行的超时时间
     */
    public static final int EXECUTE_TIME_OUT = 1000 * 60 * 5;

    public List<Snapshot> executeGetSnapshots(String computerName, String vmName) throws IOException {
        List<Snapshot> snapshotList = new ArrayList<>();
        //Get-VMSnapshot -VMName Win7
        String cmd = "Get-VMSnapshot  -ComputerName " + computerName + " -VMName  " + vmName + " | Select Name | ConvertTo-csv";
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);
        //  | ConvertTo-csv
        CommandLine cl = CommandLine.parse("powershell  " + cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        if (exec.isFailure(result)) {
            LOGGER.error("命令执行失败!!!");
            return null;
        }
        /*
        #TYPE Selected.Microsoft.HyperV.PowerShell.VirtualMachine
        "VMName","VMId","State","Uptime","ComputerName","CreationTime"
        "Win7","fcb6849b-a93b-4776-850b-202737bbc951","Running","11:07:34","Fxz-01507041630","2015/8/11 星期二 上午 1:26:32"
        "WindownsXP","918a32b7-4b4b-4987-917d-066f2f77de3b","Off","00:00:00","Fxz-01507041630","2015/8/10 星期一 上午 10:30:17"
         */

        final CSVParser parser = new CSVParser(new StringReader(stdout.toString()), CSVFormat.RFC4180.withHeader());

        try {
            for (CSVRecord record : parser.getRecords()) {
                if (record.get(0).equals("Name")) {
                    continue;
                }
                Snapshot snapshot = new Snapshot();
                snapshot.setName(record.get(0));
                snapshotList.add(snapshot);
            }
        } finally {
            parser.close();
            stdout.close();
        }
        return snapshotList;
    }


    public List<Vps> executeGetAllVps() throws IOException {
        LOGGER.info("VpsGetAllCommand start executeGetAllVps...");
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);
        //  | ConvertTo-csv
        CommandLine cl = CommandLine.parse("powershell    Get-VM  | select VMName,VMId,State,Uptime,ComputerName,CreationTime   | ConvertTo-csv");

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        if (exec.isFailure(result)) {
            LOGGER.error("命令执行失败!!!");
            return null;
        }
        /*
        #TYPE Selected.Microsoft.HyperV.PowerShell.VirtualMachine
        "VMName","VMId","State","Uptime","ComputerName","CreationTime"
        "Win7","fcb6849b-a93b-4776-850b-202737bbc951","Running","11:07:34","Fxz-01507041630","2015/8/11 星期二 上午 1:26:32"
        "WindownsXP","918a32b7-4b4b-4987-917d-066f2f77de3b","Off","00:00:00","Fxz-01507041630","2015/8/10 星期一 上午 10:30:17"
         */

        final CSVParser parser = new CSVParser(new StringReader(stdout.toString()), CSVFormat.RFC4180.withHeader());
        List<Vps> vpsList = new ArrayList<>();
        try {
            for (CSVRecord record : parser.getRecords()) {
                if (record.get(0).equals("VMName")) {
                    continue;
                }
                String vmName = record.get(0);
                String vmId = record.get(1);
                String state = record.get(2);
                String uptime = record.get(3);
                String computerName = record.get(4);
                String creationTime = record.get(5);

                Vps vps = new Vps();
                vps.setStatus(state);
                vps.setVmName(vmName);
                vps.setVmId(vmId);
                vps.setUptime(uptime);
                vps.setComputerName(computerName);
                vps.setCreationTime(creationTime);

                vpsList.add(vps);
            }
        } finally {
            parser.close();
            stdout.close();
        }
        return vpsList;
    }

    public ResultVo executeRestart(String computerName, String vmName) throws IOException {
        LOGGER.info("executeRestart: computerName=" + computerName + ",vmName=" + vmName);
        //1. 先获取vm的运行状态
        // Get-VM -ComputerName "Fxz-01507041630" -Name "Win7"
        String state = this.executeGetState(computerName, vmName);
        ResultVo resultVo = null;
        if (state.equals("Running")) {
            resultVo = this.executeStop(computerName, vmName, false);
        } else if (!state.equals("Off")) {
            resultVo = this.executeStop(computerName, vmName, true);
        }
        if (resultVo != null) {
            LOGGER.info(">>>>>executeStop:" + resultVo.toString());
        }
        ResultVo resultVo1 = this.executeStart(computerName, vmName);
        if(resultVo1.isSucc()){
            resultVo1.setResult("重启成功");
        }
        return resultVo1;
    }

    private String executeGetState(String computerName, String vmName) throws IOException {
        LOGGER.info("executeGetState: computerName=" + computerName + ",vmName=" + vmName);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);
        //   Get-VM -ComputerName "Fxz-01507041630" -Name "Win7 | ConvertTo-csv"
        String cmd = "powershell Get-VM  -ComputerName " + computerName + " -Name " + vmName + "  | select State | ConvertTo-csv";
        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        String state = null;

        if (exec.isFailure(result)) {
            LOGGER.error("Start命令执行失败!!!");
            return state;
        }
        final CSVParser parser = new CSVParser(new StringReader(stdout.toString()), CSVFormat.RFC4180.withHeader());

        try {
            for (CSVRecord record : parser.getRecords()) {
                if (record.get(0).equals("State")) {
                    continue;
                }
                state = record.get(0);
            }
        } finally {
            parser.close();
            stdout.close();
        }
        return state;
    }

    public ResultVo executeStart(String computerName, String vmName) throws IOException {
        LOGGER.info("executeStart: computerName=" + computerName + ",vmName=" + vmName);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);
        //   Stop-VM -ComputerName "Fxz-01507041630" -Name "Win7"
        String cmd = new StringBuffer("powershell Start-VM -ComputerName ").append(computerName).append(" -Name ").append(vmName).toString();
        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        ResultVo resultVo = new ResultVo();
        resultVo.setResult("开机成功");
        resultVo.setSucc(true);
        if (exec.isFailure(result)) {
            LOGGER.error("Start命令执行失败!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        stdout.close();
        return resultVo;
    }

    public ResultVo executeStop(String computerName, String vmName, boolean force) throws IOException {
        LOGGER.info("executeStop: computerName=" + computerName + ",vmName=" + vmName);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);
        //   Stop-VM -ComputerName "Fxz-01507041630" -Name "Win7"
        StringBuffer stringBuffer = new StringBuffer("powershell Stop-VM -ComputerName ").append(computerName).append(" -Name ").append(vmName);
        if (force) {
            stringBuffer.append(" -Force");
        }
        CommandLine cl = CommandLine.parse(stringBuffer.toString());

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);
        ResultVo resultVo = new ResultVo();
        resultVo.setResult("关机成功");
        resultVo.setSucc(true);

        if (exec.isFailure(result)) {
            LOGGER.error("Stop命令执行失败!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        stdout.close();
        resultVo.setSucc(true);
        return resultVo;
    }

    /**
     * 创建快照
     *
     * @param computerName
     * @param vmName
     * @return
     * @throws IOException
     */
    public ResultVo executeCreateSnapshot(String computerName, String vmName) throws IOException {
        LOGGER.info("executeStart: computerName=" + computerName + ",vmName=" + vmName);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);

        // Get-VM -Name Win7 | CHECKPOINT-VM -SnapshotName Win7_Snap_Test1
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd/HH/mm/ss");//可以方便地修改日期格式
        String time = dateFormat.format( now );
        String cmd = "powershell Get-VM -Name " + vmName + " -ComputerName " + computerName + " | CHECKPOINT-VM -SnapshotName " + vmName + "_" + time + "_Snapshot";

        LOGGER.info("CMD:" + cmd
        );

        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        ResultVo resultVo = new ResultVo();
        resultVo.setResult("备份成功");
        resultVo.setSucc(true);
        if (exec.isFailure(result)) {
            LOGGER.error("备份命令执行失败!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        stdout.close();
        return resultVo;

    }
    /**
     * 删除该虚拟机所有快照
     *
     * @param computerName
     * @param vmName
     * @return
     */
    public ResultVo executeRemoveSnapshot(String computerName, String vmName) throws IOException {
        LOGGER.info("executeRemoveSnapshot: computerName=" + computerName + ",vmName=" + vmName);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);

        // Get-VM -Name Win7 | CHECKPOINT-VM -SnapshotName Win7_Snap_Test1
        String cmd = "powershell Get-VM -Name " + vmName + " -ComputerName " + computerName + " | Remove-VMSnapshot";

        LOGGER.info("CMD:" + cmd
        );

        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        ResultVo resultVo = new ResultVo();
        resultVo.setResult("删除成功");
        resultVo.setSucc(true);
        if (exec.isFailure(result)) {
            LOGGER.error("删除快照命令执行失败!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        stdout.close();
        return resultVo;
    }
    /**
     * 恢复最近快照
     *
     * @param computerName
     * @param vmName
     * @return
     */
    public ResultVo executeRestoreSnapshot(String computerName, String vmName) throws IOException {
        LOGGER.info("executeRestoreSnapshot: computerName=" + computerName + ",vmName=" + vmName);

        LOGGER.info("第一步: 获取最新快照的名称....");
        List<Snapshot> snapshotList = this.executeGetSnapshots(computerName, vmName);

        ResultVo resultVo = new ResultVo();
        if (snapshotList != null && snapshotList.size() == 0) {
            resultVo.setSucc(false);
            resultVo.setResult("该vps暂无快照,无法进行快照恢复. 如有需要帮助，请联系管理员");
            return resultVo;
        }
        //获取最新快照的名称
        String snapName = snapshotList.get(snapshotList.size() - 1).getName();

        LOGGER.info("最新快照名称:" + snapName);

        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);

        String cmd = "powershell  Restore-VMSnapshot -Name " + snapName + " -VMName " + vmName + " -ComputerName " + computerName + " -confirm:$False";
        LOGGER.info("CMD:" + cmd);
        LOGGER.info("CMD:" + String.valueOf(snapshotList.size()));

        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        resultVo.setResult("系统恢复成功");
        resultVo.setSucc(true);
        if (exec.isFailure(result)) {
            LOGGER.error("系统恢复失败,请重试!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        stdout.close();
        return resultVo;
    }
    /**
     * 恢复最早快照 系统初始化
     *
     * @param computerName
     * @param vmName
     * @return
     */
    public ResultVo executeInitVpsSnapshot(String computerName, String vmName) throws IOException {
        LOGGER.info("executeRestoreSnapshot: computerName=" + computerName + ",vmName=" + vmName);

        LOGGER.info("first: get the oldest snapshot name....");
        List<Snapshot> snapshotList = this.executeGetSnapshots(computerName, vmName);

        ResultVo resultVo = new ResultVo();
        if (snapshotList != null && snapshotList.size() == 0) {
            resultVo.setSucc(false);
            resultVo.setResult("该vps暂无快照,无法进行快照恢复. 如有需要帮助，请联系管理员");
            return resultVo;
        }
        //获取最新快照的名称
        //String snapName = snapshotList.get(snapshotList.size() - 1).getName();
        //获取最早快照的名称
        String snapName = snapshotList.get(0).getName();
        ExecuteWatchdog watchdog = new ExecuteWatchdog(EXECUTE_TIME_OUT);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        PumpStreamHandler psh = new PumpStreamHandler(stdout);

        String cmd = "powershell  Restore-VMSnapshot -Name " + snapName + " -VMName " + vmName + " -ComputerName " + computerName + " -confirm:$False";
        LOGGER.info("CMD:" + cmd);

        CommandLine cl = CommandLine.parse(cmd);

        DefaultExecutor exec = new DefaultExecutor();
        exec.setWatchdog(watchdog);

        exec.setStreamHandler(psh);
        int result = exec.execute(cl);

        resultVo.setResult("系统恢复成功");
        resultVo.setSucc(true);
        if (exec.isFailure(result)) {
            LOGGER.error("系统恢复失败,请重试!!!");
            resultVo.setSucc(false);
            return resultVo;
        }
        executeRemoveSnapshot(computerName,vmName);//清空所有快照
        executeCreateSnapshot(computerName,vmName);//创建快照
        stdout.close();
        return resultVo;
    }
}
